<template>
    <!-- #ifndef APP-NVUE -->
    <view
        v-show="isShow"
        ref="ani"
        :animation="animationData"
        :class="customClass"
        :style="transformStyles"
        @click="onClick"
        ><slot></slot
    ></view>
    <!-- #endif -->
    <!-- #ifdef APP-NVUE -->
    <view
        v-if="isShow"
        ref="ani"
        :animation="animationData"
        :class="customClass"
        :style="transformStyles"
        @click="onClick"
        ><slot></slot
    ></view>
    <!-- #endif -->
</template>

<script>
import { createAnimation } from "./createAnimation";

/**
 * Transition 过渡动画
 * @description 简单过渡动画组件
 * @tutorial https://ext.dcloud.net.cn/plugin?id=985
 * @property {Boolean} show = [false|true] 控制组件显示或隐藏
 * @property {Array|String} modeClass = [fade|slide-top|slide-right|slide-bottom|slide-left|zoom-in|zoom-out] 过渡动画类型
 *  @value fade 渐隐渐出过渡
 *  @value slide-top 由上至下过渡
 *  @value slide-right 由右至左过渡
 *  @value slide-bottom 由下至上过渡
 *  @value slide-left 由左至右过渡
 *  @value zoom-in 由小到大过渡
 *  @value zoom-out 由大到小过渡
 * @property {Number} duration 过渡动画持续时间
 * @property {Object} styles 组件样式，同 css 样式，注意带’-‘连接符的属性需要使用小驼峰写法如：`backgroundColor:red`
 */
export default {
    name: "UniTransition",
    props: {
        show: {
            type: Boolean,
            default: false
        },
        modeClass: {
            type: [Array, String],
            default() {
                return "fade";
            }
        },
        duration: {
            type: Number,
            default: 300
        },
        styles: {
            type: Object,
            default() {
                return {};
            }
        },
        customClass: {
            type: String,
            default: ""
        },
        onceRender: {
            type: Boolean,
            default: false
        }
    },
    emits: ["click", "change"],
    data() {
        return {
            isShow: false,
            transform: "",
            opacity: 1,
            animationData: {},
            durationTime: 300,
            config: {}
        };
    },
    computed: {
        // 生成样式数据
        stylesObject() {
            let styles = {
                ...this.styles,
                "transition-duration": this.duration / 1000 + "s"
            };
            let transform = "";
            for (let i in styles) {
                let line = this.toLine(i);
                transform += line + ":" + styles[i] + ";";
            }
            return transform;
        },
        // 初始化动画条件
        transformStyles() {
            return "transform:" + this.transform + ";" + "opacity:" + this.opacity + ";" + this.stylesObject;
        }
    },
    watch: {
        show: {
            handler(newVal) {
                if (newVal) {
                    this.open();
                } else {
                    // 避免上来就执行 close,导致动画错乱
                    if (this.isShow) {
                        this.close();
                    }
                }
            },
            immediate: true
        }
    },
    created() {
        // 动画默认配置
        this.config = {
            duration: this.duration,
            timingFunction: "ease",
            transformOrigin: "50% 50%",
            delay: 0
        };
        this.durationTime = this.duration;
    },
    methods: {
        /**
         *  ref 触发 初始化动画
         */
        init(obj = {}) {
            if (obj.duration) {
                this.durationTime = obj.duration;
            }
            this.animation = createAnimation(Object.assign(this.config, obj), this);
        },
        /**
         * 点击组件触发回调
         */
        onClick() {
            this.$emit("click", {
                detail: this.isShow
            });
        },
        /**
         * ref 触发 动画分组
         * @param {Object} obj
         */
        step(obj, config = {}) {
            if (!this.animation) return;
            for (let i in obj) {
                try {
                    if (typeof obj[i] === "object") {
                        this.animation[i](...obj[i]);
                    } else {
                        this.animation[i](obj[i]);
                    }
                } catch (e) {
                    console.error(`方法 ${i} 不存在`);
                }
            }
            this.animation.step(config);
            return this;
        },
        /**
         *  ref 触发 执行动画
         */
        run(fn) {
            if (!this.animation) return;
            this.animation.run(fn);
        },
        // 开始过度动画
        open() {
            clearTimeout(this.timer);
            this.transform = "";
            this.isShow = true;
            let { opacity, transform } = this.styleInit(false);
            if (typeof opacity !== "undefined") {
                this.opacity = opacity;
            }
            this.transform = transform;
            // 确保动态样式已经生效后，执行动画，如果不加 nextTick ，会导致 wx 动画执行异常
            this.$nextTick(() => {
                // TODO 定时器保证动画完全执行，目前有些问题，后面会取消定时器
                this.timer = setTimeout(() => {
                    this.animation = createAnimation(this.config, this);
                    this.tranfromInit(false).step();
                    this.animation.run();
                    this.$emit("change", {
                        detail: this.isShow
                    });
                }, 20);
            });
        },
        // 关闭过度动画
        close(type) {
            if (!this.animation) return;
            this.tranfromInit(true)
                .step()
                .run(() => {
                    this.isShow = false;
                    this.animationData = null;
                    this.animation = null;
                    let { opacity, transform } = this.styleInit(false);
                    this.opacity = opacity || 1;
                    this.transform = transform;
                    this.$emit("change", {
                        detail: this.isShow
                    });
                });
        },
        // 处理动画开始前的默认样式
        styleInit(type) {
            let styles = {
                transform: ""
            };
            let buildStyle = (type, mode) => {
                if (mode === "fade") {
                    styles.opacity = this.animationType(type)[mode];
                } else {
                    styles.transform += this.animationType(type)[mode] + " ";
                }
            };
            if (typeof this.modeClass === "string") {
                buildStyle(type, this.modeClass);
            } else {
                this.modeClass.forEach((mode) => {
                    buildStyle(type, mode);
                });
            }
            return styles;
        },
        // 处理内置组合动画
        tranfromInit(type) {
            let buildTranfrom = (type, mode) => {
                let aniNum = null;
                if (mode === "fade") {
                    aniNum = type ? 0 : 1;
                } else {
                    aniNum = type ? "-100%" : "0";
                    if (mode === "zoom-in") {
                        aniNum = type ? 0.8 : 1;
                    }
                    if (mode === "zoom-out") {
                        aniNum = type ? 1.2 : 1;
                    }
                    if (mode === "slide-right") {
                        aniNum = type ? "100%" : "0";
                    }
                    if (mode === "slide-bottom") {
                        aniNum = type ? "100%" : "0";
                    }
                }
                this.animation[this.animationMode()[mode]](aniNum);
            };
            if (typeof this.modeClass === "string") {
                buildTranfrom(type, this.modeClass);
            } else {
                this.modeClass.forEach((mode) => {
                    buildTranfrom(type, mode);
                });
            }

            return this.animation;
        },
        animationType(type) {
            return {
                fade: type ? 0 : 1,
                "slide-top": `translateY(${type ? "0" : "-100%"})`,
                "slide-right": `translateX(${type ? "0" : "100%"})`,
                "slide-bottom": `translateY(${type ? "0" : "100%"})`,
                "slide-left": `translateX(${type ? "0" : "-100%"})`,
                "zoom-in": `scaleX(${type ? 1 : 0.8}) scaleY(${type ? 1 : 0.8})`,
                "zoom-out": `scaleX(${type ? 1 : 1.2}) scaleY(${type ? 1 : 1.2})`
            };
        },
        // 内置动画类型与实际动画对应字典
        animationMode() {
            return {
                fade: "opacity",
                "slide-top": "translateY",
                "slide-right": "translateX",
                "slide-bottom": "translateY",
                "slide-left": "translateX",
                "zoom-in": "scale",
                "zoom-out": "scale"
            };
        },
        // 驼峰转中横线
        toLine(name) {
            return name.replace(/([A-Z])/g, "-$1").toLowerCase();
        }
    }
};
</script>

<style></style>
